#include <iostream>
#include <memory>
#include <vector>

#include <unistd.h>
#include <pthread.h>

#include "Thread.hpp"
#include "Mutex.hpp"

// void* start_routine(void* args)
// {
//     const std::string str = static_cast<const char*>(args);
//     while(true)
//     {
//         std::cout << "thread-" << str << std::endl;
//         sleep(1);
//     }
// }

// int main()
// {
//     std::unique_ptr<Thread> thread1(new Thread(start_routine, (void*)"mythreadone", 1));
//     std::unique_ptr<Thread> thread2(new Thread(start_routine, (void*)"mythreadtwo", 2));
//     std::unique_ptr<Thread> thread3(new Thread(start_routine, (void*)"mythreadthree", 3));

//     // thread1->start();
//     // thread2->start();
//     // thread3->start();

//     thread1->join();
//     thread2->join();
//     thread3->join();

//     return 0;
// }

// int tickets = 10000;
int tickets = 10;

/*
欲使出现多个线程抢同一张票，需使各线程交叉执行。
多个线程交叉执行的本质就是让调度器尽可能频繁调度切换线程。
线程一般在时间片到了发生切换，或者来了更高优先级的线程，或者线程等待。
线程从内核态返回用户态时，要对调度状态进行检测，若条件允许，直接发生线程切换。（时线程检测，线程共享进程地址空间，
地址空间中分为用户态和内核态，线程是切换到内核态进行检测，这样实际就是os检测）
*/

/*
加锁和解锁是多个线程串行执行的，所以相对于不加锁，程序的执行变慢了。
锁只规定各线程对临界资源的互斥访问，没有规定哪个线程先执行。
*/
// 1. 全局锁
// pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;// 互斥锁

// void* grabTicket(void* args)
// {
//     const std::string user = static_cast<const char*>(args);
//     while(true)
//     {
//         // 对临界区进行加锁
//         pthread_mutex_lock(&lock);
//         // pthread_mutex_lock(&lock);// 造成死锁

//         if(tickets > 0)
//         {
//             // 先让线程等待，以让调度器调度切换
//             usleep(1222);// 模拟真实抢票花费的时间
//             // 1s = 1000ms = 100 0000us

//             // 有余票，能抢
//             std::cout << user << " -> 正在进行抢票：" << tickets-- << std::endl;

//             // 解锁
//             pthread_mutex_unlock(&lock);
//         }
//         else
//         {
//             // 解锁
//             pthread_mutex_unlock(&lock);
//             break;
//         }
//         // 抢票结束后
//         usleep(10000);
//     }
// }

// 2. 局部锁
// class ThreadData
// {
// public:
//     ThreadData(const std::string& name, pthread_mutex_t* mutex)
//         : _threadName(name)
//         , _mutex(mutex)
//     {}
//     ~ThreadData()
//     {}
// public:
//     std::string _threadName;
//     pthread_mutex_t* _mutex;
// };
// void* grabTicket(void* args)
// {
//     ThreadData* td = static_cast<ThreadData*>(args);
//     while(true)
//     {
//         // 对临界区进行加锁
//         pthread_mutex_lock(td->_mutex);
//         if(tickets > 0)
//         {
//             // 先让线程等待，以让调度器调度切换
//             usleep(1222);// 模拟真实抢票花费的时间
//             // 1s = 1000ms = 100 0000us

//             // 有余票，能抢
//             std::cout << td->_threadName << " -> 正在进行抢票：" << tickets-- << std::endl;

//             // 解锁
//             pthread_mutex_unlock(td->_mutex);
//         }
//         else
//         {
//             // 解锁
//             pthread_mutex_unlock(td->_mutex);
//             break;
//         }
//         // 抢票结束后
//         usleep(10000);
//     }
// }

// 3. 全局锁的自动加锁和解锁
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; // 互斥锁

void *grabTicket(void *args)
{
    const std::string user = static_cast<const char *>(args);
    while (true)
    {
        // 对临界区进行加锁
        {
            LockGuard lockgard(&lock);
            // LockGuard对象lockgard初始化时，调用构造函数，构造函数中会加锁，
            // 出了while的一个循环作用域后就自动调用析构函数销毁了，而析构函数中会解锁
            // 这是RAII风格的加锁和解锁
            if (tickets > 0)
            {
                // 先让线程等待，以让调度器调度切换
                usleep(1222); // 模拟真实抢票花费的时间
                // 1s = 1000ms = 100 0000us

                // 有余票，能抢
                std::cout << user << " -> 正在进行抢票：" << tickets-- << std::endl;
                lockgard.~LockGuard();
            }
            else
                break;
        }

        // 抢票结束后
        usleep(1000);
    }
}

int main()
{
    // std::unique_ptr<Thread> thread1(new Thread(grabTicket, (void*)"user1", 1));
    // std::unique_ptr<Thread> thread2(new Thread(grabTicket, (void*)"user2", 2));
    // std::unique_ptr<Thread> thread3(new Thread(grabTicket, (void*)"user3", 3));

    // thread1->join();
    // thread2->join();
    // thread3->join();

    // 1. 全局锁
    pthread_t t1, t2, t3;
    pthread_create(&t1, nullptr, grabTicket, (void *)"thread_1");
    pthread_create(&t2, nullptr, grabTicket, (void *)"thread_2");
    pthread_create(&t3, nullptr, grabTicket, (void *)"thread_3");
    pthread_join(t1, nullptr);
    pthread_join(t2, nullptr);
    pthread_join(t3, nullptr);

#define NUM 3
    // 2. 局部锁
    // pthread_mutex_t lock;
    // pthread_mutex_init(&lock, nullptr);// 局部锁的初始化
    // std::vector<pthread_t> tids(NUM);
    // for(int i = 0; i < NUM; ++i)
    // {
    //     char buffer[64];
    //     snprintf(buffer, sizeof(buffer) - 1, "thread_%d", i + 1);
    //     ThreadData* td = new ThreadData(buffer, &lock);
    //     pthread_create(&tids[i], nullptr, grabTicket, td);
    // }
    // for(const auto& tid : tids)
    // {
    //     pthread_join(tid, nullptr);
    // }

    return 0;
}